package com.sunjy.common.utils;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * Created by chenzhang on 2016/9/8.
 */
public class RsaUtil {

    private static final Logger logger = LoggerFactory.getLogger(RsaUtil.class);

    private static int RSA_ENCRYPT_MAX_LENGTH = 117;

    private static int RSA_DECRYPT_MAX_LENGTH = 128;

    /**
     * RSA算法实现
     *
     * @param src  源字节
     * @param key  公钥/私钥
     * @param mode 加密/解密
     * @return
     */
    private static byte[] algorithm4RSA(byte[] src, Key key, int mode) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(mode, key);
            // 分段加密 RSA加密明文最大长度 117,解密最大密文长度128
            int blockSize = (mode == Cipher.ENCRYPT_MODE) ? RSA_ENCRYPT_MAX_LENGTH : RSA_DECRYPT_MAX_LENGTH;
            byte[] result = null;
            for (int i = 0; i < src.length; i += blockSize) {
                // 注意要使用2的倍数，否则会出现加密后的内容再解密时为乱码
                byte[] doFinal = cipher.doFinal(subarray(src, i, i + blockSize));
                result = addAll(result, doFinal);
            }
            return result;
        } catch (Exception e) {
            logger.error("解密失败", e);
        }
        return null;
    }

    /**
     * 加密
     *
     * @param plain 明文
     * @param key   公钥/私钥
     * @return
     */
    public static String encrypt4RSA(String plain, Key key) {
        String cipherText = null;
        //转BASE64
        byte[] plain2base64 = Base64.encodeBase64(plain.getBytes());
        //加密
        byte[] result = algorithm4RSA(plain2base64, key, Cipher.ENCRYPT_MODE);
        if (result == null) return null;

        cipherText = byte2Hex(result);
        return cipherText;
    }

    /**
     * 将string类型转换为PublicKey
     * @param key
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = (new BASE64Decoder()).decodeBuffer(key);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }


    /**
     * 将string类型转换为PrivateKey
     * @param key
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = (new BASE64Decoder()).decodeBuffer(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }
    /**
     * 解密
     *
     * @param cipher 密文
     * @param key    公钥/私钥
     * @return
     */
    public static String decrypt4RSA(String cipher, Key key) {
        String plainText = null;
        byte[] cipher2Byte = hex2Bytes(cipher);

        byte[] result = algorithm4RSA(cipher2Byte, key, Cipher.DECRYPT_MODE);
        if (result == null) return null;
        plainText = new String(Base64.decodeBase64(result));
        return plainText;
    }


    private static byte[] subarray(byte[] array, int startIndexInclusive, int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return new byte[0];
        }

        byte[] subarray = new byte[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    private static byte[] addAll(byte[] array1, byte[] array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        byte[] joinedArray = new byte[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    private static byte[] clone(byte[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    private static String byte2Hex(byte[] srcBytes) {
        StringBuilder hexRetSB = new StringBuilder();
        for (byte b : srcBytes) {
            String hexString = Integer.toHexString(0x00ff & b);
            hexRetSB.append(hexString.length() == 1 ? 0 : "").append(hexString);
        }
        return hexRetSB.toString();
    }

    private static byte[] hex2Bytes(String source) {
        byte[] sourceBytes = new byte[source.length() / 2];
        for (int i = 0; i < sourceBytes.length; i++) {
            sourceBytes[i] = (byte) Integer.parseInt(source.substring(i * 2, i * 2 + 2), 16);
        }
        return sourceBytes;
    }





}
